#version 300 es
#define LOG_DEPTH
#define OES_texture_float_linear

#define OES_texture_float

#line 0
const float czm_infinity = 5906376272000.0;
uniform vec2 czm_currentFrustum;
uniform mat4 czm_inverseProjection;
uniform vec3 czm_sunDirectionWC;
uniform vec3 czm_lightDirectionWC;
uniform mat4 czm_inverseModelView;
struct czm_ray {
    vec3 origin;
    vec3 direction;
};
struct czm_raySegment {
    float start;
    float stop;
};
const czm_raySegment czm_emptyRaySegment = czm_raySegment(-czm_infinity, -czm_infinity);
const czm_raySegment czm_fullRaySegment = czm_raySegment(0.0, czm_infinity);
#ifdef LOG_DEPTH
    
    out float v_depthFromNearPlusOne;
    #ifdef SHADOW_MAP
        out vec3 v_logPositionEC;
    #endif
#endif

vec4 czm_updatePositionDepth(vec4 coords) {
    #if defined(LOG_DEPTH)
        
        #ifdef SHADOW_MAP
            vec3 logPositionEC = (czm_inverseProjection * coords).xyz;
            v_logPositionEC = logPositionEC;
        #endif
        
        
        
        
        
        
        
        
        coords.z = clamp(coords.z / coords.w, -1.0, 1.0) * coords.w;
    #endif
    
    return coords;
}
void czm_vertexLogDepth() {
    #ifdef LOG_DEPTH
        v_depthFromNearPlusOne = (gl_Position.w - czm_currentFrustum.x) + 1.0;
        gl_Position = czm_updatePositionDepth(gl_Position);
    #endif
}
void czm_vertexLogDepth(vec4 clipCoords) {
    #ifdef LOG_DEPTH
        v_depthFromNearPlusOne = (clipCoords.w - czm_currentFrustum.x) + 1.0;
        czm_updatePositionDepth(clipCoords);
    #endif
}
uniform mat4 czm_modelViewProjection;
vec3 czm_getDynamicAtmosphereLightDirection(vec3 positionWC, float lightEnum) {
    const float NONE = 0.0;
    const float SCENE_LIGHT = 1.0;
    const float SUNLIGHT = 2.0;
    vec3 lightDirection = positionWC * float(lightEnum == NONE) +
    czm_lightDirectionWC * float(lightEnum == SCENE_LIGHT) +
    czm_sunDirectionWC * float(lightEnum == SUNLIGHT);
    return normalize(lightDirection);
}
uniform mat4 czm_model;
uniform vec3 czm_ellipsoidInverseRadii;
czm_raySegment czm_rayEllipsoidIntersectionInterval(czm_ray ray, vec3 ellipsoid_center, vec3 ellipsoid_inverseRadii) {
    vec3 q = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.origin, 1.0)).xyz;
    vec3 w = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.direction, 0.0)).xyz;
    q = q - ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ellipsoid_center, 1.0)).xyz;
    float q2 = dot(q, q);
    float qw = dot(q, w);
    if (q2 > 1.0) {
        if (qw >= 0.0) {
            return czm_emptyRaySegment;
        }
        else {
            float qw2 = qw * qw;
            float difference = q2 - 1.0;
            float w2 = dot(w, w);
            float product = w2 * difference;
            if (qw2 < product) {
                return czm_emptyRaySegment;
            }
            else if (qw2 > product) {
                float discriminant = qw * qw - product;
                float temp = -qw + sqrt(discriminant);
                float root0 = temp / w2;
                float root1 = difference / temp;
                if (root0 < root1) {
                    czm_raySegment i = czm_raySegment(root0, root1);
                    return i;
                }
                else {
                    czm_raySegment i = czm_raySegment(root1, root0);
                    return i;
                }

            }
            else {
                float root = sqrt(difference / w2);
                czm_raySegment i = czm_raySegment(root, root);
                return i;
            }

        }

    }
    else if (q2 < 1.0) {
        float difference = q2 - 1.0;
        float w2 = dot(w, w);
        float product = w2 * difference;
        float discriminant = qw * qw - product;
        float temp = -qw + sqrt(discriminant);
        czm_raySegment i = czm_raySegment(0.0, temp / w2);
        return i;
    }
    else {
        if (qw < 0.0) {
            float w2 = dot(w, w);
            czm_raySegment i = czm_raySegment(0.0, -qw / w2);
            return i;
        }
        else {
            return czm_emptyRaySegment;
        }

    }

}
uniform float czm_eyeHeight;
uniform vec3 czm_ellipsoidRadii;
uniform vec3 czm_viewerPositionWC;
float czm_approximateTanh(float x) {
    float x2 = x * x;
    return max(-1.0, min(1.0, x * (27.0 + x2) / (27.0 + 9.0 * x2)));
}
czm_raySegment czm_raySphereIntersectionInterval(czm_ray ray, vec3 center, float radius) {
    vec3 o = ray.origin;
    vec3 d = ray.direction;
    vec3 oc = o - center;
    float a = dot(d, d);
    float b = 2.0 * dot(d, oc);
    float c = dot(oc, oc) - (radius * radius);
    float det = (b * b) - (4.0 * a * c);
    if (det < 0.0) {
        return czm_emptyRaySegment;
    }
    float sqrtDet = sqrt(det);
    float t0 = (-b - sqrtDet) / (2.0 * a);
    float t1 = (-b + sqrtDet) / (2.0 * a);
    czm_raySegment result = czm_raySegment(t0, t1);
    return result;
}
#line 0
uniform vec3 u_radiiAndDynamicAtmosphereColor;
uniform float u_atmosphereLightIntensity;
uniform float u_atmosphereRayleighScaleHeight;
uniform float u_atmosphereMieScaleHeight;
uniform float u_atmosphereMieAnisotropy;
uniform vec3 u_atmosphereRayleighCoefficient;
uniform vec3 u_atmosphereMieCoefficient;
const float ATMOSPHERE_THICKNESS = 111e3;
const int PRIMARY_STEPS_MAX = 16;
const int LIGHT_STEPS_MAX = 4;
// 实现瑞利散射和米氏散射的主函数
void computeScattering(
czm_ray primaryRay, float primaryRayLength, vec3 lightDirection, float atmosphereInnerRadius, out vec3 rayleighColor, out vec3 mieColor, out float opacity
) {
    rayleighColor = vec3(0.0);
    mieColor = vec3(0.0);
    opacity = 0.0;
    float atmosphereOuterRadius = atmosphereInnerRadius + ATMOSPHERE_THICKNESS;
    vec3 origin = vec3(0.0);
    czm_raySegment primaryRayAtmosphereIntersect = czm_raySphereIntersectionInterval(primaryRay, origin, atmosphereOuterRadius);
    if (primaryRayAtmosphereIntersect == czm_emptyRaySegment) {
        return;
    }
    float x = 1e-7 * primaryRayAtmosphereIntersect.stop / length(primaryRayLength);
    float w_stop_gt_lprl = 0.5 * (1.0 + czm_approximateTanh(x));
    float start_0 = primaryRayAtmosphereIntersect.start;
    primaryRayAtmosphereIntersect.start = max(primaryRayAtmosphereIntersect.start, 0.0);
    primaryRayAtmosphereIntersect.stop = min(primaryRayAtmosphereIntersect.stop, length(primaryRayLength));
    float x_o_a = start_0 - ATMOSPHERE_THICKNESS;
    float w_inside_atmosphere = 1.0 - 0.5 * (1.0 + czm_approximateTanh(x_o_a));
    int PRIMARY_STEPS = PRIMARY_STEPS_MAX - int(w_inside_atmosphere * 12.0);
    int LIGHT_STEPS = LIGHT_STEPS_MAX - int(w_inside_atmosphere * 2.0);
    float rayPositionLength = primaryRayAtmosphereIntersect.start;
    float totalRayLength = primaryRayAtmosphereIntersect.stop - rayPositionLength;
    float rayStepLengthIncrease = w_inside_atmosphere * ((1.0 - w_stop_gt_lprl) * totalRayLength / (float(PRIMARY_STEPS * (PRIMARY_STEPS + 1)) / 2.0));
    float rayStepLength = max(1.0 - w_inside_atmosphere, w_stop_gt_lprl) * totalRayLength / max(7.0 * w_inside_atmosphere, float(PRIMARY_STEPS));
    vec3 rayleighAccumulation = vec3(0.0);
    vec3 mieAccumulation = vec3(0.0);
    vec2 opticalDepth = vec2(0.0);
    vec2 heightScale = vec2(u_atmosphereRayleighScaleHeight, u_atmosphereMieScaleHeight);
    for (int i = 0; i < PRIMARY_STEPS_MAX; ++i) {
        if (i >= PRIMARY_STEPS) {
            break;
        }
        vec3 samplePosition = primaryRay.origin + primaryRay.direction * (rayPositionLength + rayStepLength);
        float sampleHeight = length(samplePosition) - atmosphereInnerRadius;
        vec2 sampleDensity = exp(-sampleHeight / heightScale) * rayStepLength;
        opticalDepth += sampleDensity;
        czm_ray lightRay = czm_ray(samplePosition, lightDirection);
        czm_raySegment lightRayAtmosphereIntersect = czm_raySphereIntersectionInterval(lightRay, origin, atmosphereOuterRadius);
        float lightStepLength = lightRayAtmosphereIntersect.stop / float(LIGHT_STEPS);
        float lightPositionLength = 0.0;
        vec2 lightOpticalDepth = vec2(0.0);
        for (int j = 0; j < LIGHT_STEPS_MAX; ++j) {
            if (j >= LIGHT_STEPS) {
                break;
            }
            vec3 lightPosition = samplePosition + lightDirection * (lightPositionLength + lightStepLength * 0.5);
            float lightHeight = length(lightPosition) - atmosphereInnerRadius;
            lightOpticalDepth += exp(-lightHeight / heightScale) * lightStepLength;
            lightPositionLength += lightStepLength;
        }
        vec3 attenuation = exp(-((u_atmosphereMieCoefficient * (opticalDepth.y + lightOpticalDepth.y)) + (u_atmosphereRayleighCoefficient * (opticalDepth.x + lightOpticalDepth.x))));
        rayleighAccumulation += sampleDensity.x * attenuation;
        mieAccumulation += sampleDensity.y * attenuation;
        rayPositionLength += (rayStepLength += rayStepLengthIncrease);
    }
    rayleighColor = u_atmosphereRayleighCoefficient * rayleighAccumulation;
    mieColor = u_atmosphereMieCoefficient * mieAccumulation;
    opacity = length(exp(-((u_atmosphereMieCoefficient * opticalDepth.y) + (u_atmosphereRayleighCoefficient * opticalDepth.x))));
}
vec4 computeAtmosphereColor(
vec3 positionWC, vec3 lightDirection, vec3 rayleighColor, vec3 mieColor, float opacity
) {
    vec3 cameraToPositionWC = positionWC - czm_viewerPositionWC;
    vec3 cameraToPositionWCDirection = normalize(cameraToPositionWC);
    float cosAngle = dot(cameraToPositionWCDirection, lightDirection);
    float cosAngleSq = cosAngle * cosAngle;
    float G = u_atmosphereMieAnisotropy;
    float GSq = G * G;
    float rayleighPhase = 3.0 / (50.2654824574) * (1.0 + cosAngleSq);
    float miePhase = 3.0 / (25.1327412287) * ((1.0 - GSq) * (cosAngleSq + 1.0)) / (pow(1.0 + GSq - 2.0 * cosAngle * G, 1.5) * (2.0 + GSq));
    vec3 rayleigh = rayleighPhase * rayleighColor;
    vec3 mie = miePhase * mieColor;
    vec3 color = (rayleigh + mie) * u_atmosphereLightIntensity;
    return vec4(color, opacity);
}
#line 0
float interpolateByDistance(vec4 nearFarScalar, float distance) {
    float startDistance = nearFarScalar.x;
    float startValue = nearFarScalar.y;
    float endDistance = nearFarScalar.z;
    float endValue = nearFarScalar.w;
    float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0);
    return mix(startValue, endValue, t);
}
void computeAtmosphereScattering(vec3 positionWC, vec3 lightDirection, out vec3 rayleighColor, out vec3 mieColor, out float opacity, out float underTranslucentGlobe) {
    float ellipsoidRadiiDifference = czm_ellipsoidRadii.x - czm_ellipsoidRadii.z;
    float distanceAdjustMin = czm_ellipsoidRadii.x / 4.0;
    float distanceAdjustMax = czm_ellipsoidRadii.x;
    float distanceAdjustModifier = ellipsoidRadiiDifference / 2.0;
    float distanceAdjust = distanceAdjustModifier * clamp((czm_eyeHeight - distanceAdjustMin) / (distanceAdjustMax - distanceAdjustMin), 0.0, 1.0);
    float radiusAdjust = (ellipsoidRadiiDifference / 4.0) + distanceAdjust;
    float atmosphereInnerRadius = (length(czm_viewerPositionWC) - czm_eyeHeight) - radiusAdjust;
    vec3 cameraToPositionWC = positionWC - czm_viewerPositionWC;
    vec3 cameraToPositionWCDirection = normalize(cameraToPositionWC);
    czm_ray primaryRay = czm_ray(czm_viewerPositionWC, cameraToPositionWCDirection);
    underTranslucentGlobe = 0.0;
    #if defined(GLOBE_TRANSLUCENT)
        
        
        czm_raySegment primaryRayEarthIntersect = czm_raySphereIntersectionInterval(primaryRay, vec3(0.0), atmosphereInnerRadius + radiusAdjust);
        if (primaryRayEarthIntersect.start > 0.0 && primaryRayEarthIntersect.stop > 0.0) {
            vec3 direction = normalize(positionWC);
            czm_ray ellipsoidRay = czm_ray(positionWC, -direction);
            czm_raySegment ellipsoidIntersection = czm_rayEllipsoidIntersectionInterval(ellipsoidRay, vec3(0.0), czm_ellipsoidInverseRadii);
            vec3 onEarth = positionWC - (direction * ellipsoidIntersection.start);
            float angle = dot(normalize(czm_viewerPositionWC), normalize(onEarth));
            opacity = interpolateByDistance(vec4(0.0, 1.0, czm_ellipsoidRadii.x, 0.0), length(czm_viewerPositionWC - onEarth));
            vec3 horizonColor = vec3(0.1, 0.2, 0.3);
            vec3 nearColor = vec3(0.0);
            rayleighColor = mix(nearColor, horizonColor, exp(-angle) * opacity);
            underTranslucentGlobe = 1.0;
            return;
        }
    #endif
    computeScattering(
    primaryRay, length(cameraToPositionWC), lightDirection, atmosphereInnerRadius, rayleighColor, mieColor, opacity
    );
    float cameraHeight = czm_eyeHeight + atmosphereInnerRadius;
    float atmosphereOuterRadius = atmosphereInnerRadius + ATMOSPHERE_THICKNESS;
    opacity = clamp((atmosphereOuterRadius - cameraHeight) / (atmosphereOuterRadius - atmosphereInnerRadius), 0.0, 1.0);
    float nightAlpha = (u_radiiAndDynamicAtmosphereColor.z ! = 0.0) ? clamp(dot(normalize(positionWC), lightDirection), 0.0, 1.0) : 1.0;
    opacity *= pow(nightAlpha, 0.5);
}
#line 0
in vec4 position;
out vec3 v_outerPositionWC;
#ifndef PER_FRAGMENT_ATMOSPHERE
    out vec3 v_mieColor;
    out vec3 v_rayleighColor;
    out float v_opacity;
    out float v_translucent;
#endif

void czm_log_depth_main() {
    vec4 positionWC = czm_model * position;
    float lightEnum = u_radiiAndDynamicAtmosphereColor.z;
    vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC.xyz, lightEnum);
    #ifndef PER_FRAGMENT_ATMOSPHERE
        computeAtmosphereScattering(
        positionWC.xyz, lightDirection, v_rayleighColor, v_mieColor, v_opacity, v_translucent
        );
    #endif
    
    v_outerPositionWC = positionWC.xyz;
    gl_Position = czm_modelViewProjection * position;
}
#line 0
void main() {
    czm_log_depth_main();
    czm_vertexLogDepth();
}
